#!/usr/bin/python
# -*- coding:UTF-8 -*-

import os
import argparse
import sys
import json

binPaths = os.path.split(os.path.realpath(__file__))
dictPath = os.path.realpath(binPaths[0])
homePath = os.path.realpath(binPaths[0] + "/../..")
sys.path.append(homePath + "/lib")
sys.path.append(homePath + "/plugins/local/lib")


def usage():
    pname = os.path.basename(__file__)
    print(pname + " --lang <lang> eg : zh_CN or en_US ,default value zh_CN.\n")
    print("         --mode <mode> eg : truncate or append ,default value truncate.\n")
    print(" --action [beautify|remove|append|savetodb|dropcollection｜clearcollection] \n")
    print("Examples:")
    print("Remove field:")
    print('dicttool --lang en_US --action remove --filter \'{"collection": "COLLECT_INS"}\' --content BELONG_APPLICATION')
    print('dicttool --lang en_US --action append --filter \'{"collection": "COLLECT_INS"}\' --content \'{"name":"STATE", "desc":"Use State", "type":"String"}\'')
    print("dicttool --lang en_US --action beautify")
    print("dicttool --lang zh_CN --action beautify")
    print("dicttool --lang en_US --action savetodb")
    print("dicttool --lang zh_CN --action savetodb")
    print("dicttool--action dropcollection")
    print("dicttool--action clearcollection")
    exit(1)


def getDefaultFieldKeys():
    defaultKeys = [
        "_OBJ_CATEGORY",
        "_OBJ_TYPE",
        "_APP_TYPE",
        "WAS_TYPE",
        "HOSTNAME",
        "NAME",
        "SERVER_NAME",
        "INSTANCE_NAME",
        "DB_NAME",
        "UNIQUE_NAME",
        "PRODUCT_NAME",
        "MGMT_IP",
        "MGMT_PORT",
        "STATE",
        "AVAILABILITY",
        "RESPONSE_TIME",
        "ERROR_MESSAGE",
        "MAJOR_VERSION",
        "VERSION",
        "KERNEL_VERSION",
        "IP",
        "VIP",
        "PRIMARY_IP",
        "PORT",
        "SERVICE_PORTS",
        "SOFTWARE_ASSETS",
        "UPTIME",
        "IS_VIRTUAL",
        "DOMAIN",
        "SYSTEM_LOCALE",
        "TIME_ZONE",
        "INPUT_LOCALE",
        "SOCKET_PATH",
        "LISTEN",
        "RESPONSE_TIME",
        "ERROR_MESSAGE",
        "BELONG_APPLICATION",
        "BELONG_APPLICATION_MODULE",
        "RESOURCE_ID",
        "OS_ID",
        "MACHINE_ID",
        "IP_ADDRS",
        "IPV4_ADDRS",
        "IPV6_ADDRS",
        "ADMIN_PORT",
        "ADMIN_SSL_PORT",
        "MON_PORT",
        "SSL_PORT",
        "STOMP_PORT",
        "WS_PORT",
        "BRAND",
        "SYS_VENDOR",
        "VENDOR",
        "PRODUCT_UUID",
        "SN",
        "CHASSIS_SERIAL",
        "BOARD_SERIAL",
        "MODEL",
        "UPTIME",
        "OPEN_FILES_COUNT",
        "MAX_OPEN_FILES",
        "OPEN_FILES_INFO",
        "MAX_USER_PROCESS_COUNT",
        "OS_USER",
        "INSTALL_PATH",
        "CONFIG_PATH",
        "EXE_PATH",
        "JVM_TYPE",
        "JVM_VERSION",
        "JAVA_HOME",
        "MIN_HEAP_SIZE",
        "MAX_HEAP_SIZE",
        "USERS",
        "MAIN_ENV",
        "JAVA_VERSION",
        "MOUNT_POINTS",
        "NFS_MOUNTED",
        "DISKS",
        "DNS_SERVERS",
        "NTP_ENABLE",
        "NTP_SERVERS",
        "NTP_OFFSET_SECS",
        "SSH_VERSION",
        "OPENSSL_VERSION",
        "NIC_BOND",
        "ETH_INTERFACES",
        "JMX_PORT",
        "JMX_SSL",
        "CPU_COUNT",
        "CPU_USAGE",
        "CPU_USAGE_PERCORE",
        "CPU_LOAD_AVG_1",
        "CPU_LOAD_AVG_5",
        "CPU_LOAD_AVG_15",
        "CPU_QUEUE_LEN",
        "CPU_CORES",
        "CPU_LOGIC_CORES",
        "IOWAIT_PCT",
        "CPU_FREQUENCY",
        "CPU_MODE",
        "CPU_MODEL",
        "CPU_BITS",
        "CPU_ARCH",
        "CPU_VERSION",
        "CPU_FIRMWARE_VERSION",
        "CPU_MICROCODE",
        "BIOS_VERSION",
        "TOP_CPU_RPOCESSES",
        "DEFUNC_PROCESSES_COUNT",
        "MEM_AVAILABLE",
        "MEM_TOTAL",
        "MEM_USAGE",
        "MEM_USED",
        "MEM_FREE",
        "MEM_BUFFERS",
        "MEM_CACHED",
        "SWAP_TOTAL",
        "SWAP_FREE",
        "TOP_MEM_PROCESSES",
        "LISTEN_STATS",
        "CONN_STATS",
        "CONN_OUTBOUND_STATS",
        "BELONG_CLUSTER",
        "REF_DB",
        "REF_INS",
        "REF_CLUSTER",
        "REF_SERVICE",
        "REF_SWITCHES",
        "REF_FCSWITCHES",
    ]
    return defaultKeys


def correctTypeDef(fieldDef):
    subsetDef = fieldDef.get("subset", [])

    fieldType = fieldDef["type"].lower()
    if fieldType == "string":
        fieldType = "String"
    if fieldType == "text":
        fieldType = "Text"
    elif fieldType == "int":
        fieldType = "Int"
    elif fieldType == "float":
        fieldType = "Float"
    elif fieldType == "jsonarray":
        fieldType = "JsonArray"
    elif fieldType == "array":
        fieldType = "Array"
    elif fieldType == "jsonobject":
        fieldType = "JsonObject"
    fieldDef["type"] = fieldType

    if "name" in fieldDef:
        fieldName = fieldDef["name"]
        if fieldName == "AVAILABILITY":
            fieldDef["type"] = "Int"
        elif fieldName in ("COMMAND", "HEALTH_CHECK"):
            fieldDef["type"] = "Text"
        elif fieldType == "Float" and ("PID" in fieldName or "PORT" in fieldName):
            fieldDef["type"] = "Int"
        elif "IS_" in fieldName:
            fieldDef["type"] = "Int"
    else:
        if fieldType == "Float":
            fieldDef["type"] = "Int"

    if fieldType == "Array":
        if len(subsetDef) == 0:
            subsetDef = [{"name": "_value", "desc": "Value", "type": "String"}]
            fieldDef["subset"] = subsetDef
        elif len(subsetDef) > 1:
            fieldDef["type"] = "JsonArray"
        elif subsetDef[0].get("name") != "_value":
            subsetDef[0]["name"] = "_value"

    if len(subsetDef) > 0:
        for subFieldDef in fieldDef["subset"]:
            correctTypeDef(subFieldDef)


def getIcon(name):
    nameToIconMap = {
        "a10": "tsfont-a10",
        "accessendpoint": "tsfont-accessendpoint",
        "activemq": "tsfont-activemq",
        "aix": "tsfont-aix",
        "apache": "tsfont-apache",
        "db2": "tsfont-db2",
        "dbins": "tsfont-db",
        "db": "tsfont-db",
        "elasticsearch": "tsfont-elasticsearch",
        "f5": "tsfont-f5",
        "fcdev": "tsfont-fcdev",
        "fcswitch": "tsfont-fcswitch",
        "firewall": "tsfont-firewall",
        "hadoop": "tsfont-hadoop",
        "host": "tsfont-host",
        "iis": "tsfont-iis",
        "informix": "tsfont-informix",
        "informix-db": "tsfont-informix",
        "ins": "tsfont-instance",
        "java": "tsfont-java",
        "jboss": "tsfont-jboss",
        "jetty": "tsfont-jetty",
        "kafka": "tsfont-kafka",
        "keepalive": "tsfont-keepalive",
        "lighttpd": "tsfont-lighttpd",
        "linux": "tsfont-linux",
        "memcached": "tsfont-memcached",
        "mongodb": "tsfont-mongodb",
        "mssqlserver": "tsfont-mssqlserver",
        "mssqlserver-db": "tsfont-mssqlserver",
        "mysql": "tsfont-mysql",
        "mysql-db": "tsfont-mysql",
        "netdev": "tsfont-devices",
        "nginx": "tsfont-nginx",
        "novmtool": "tsfont-yunweishenjishebei",
        "oracle-rac": "tsfont-oracle-rac",
        "oracle": "tsfont-oracle",
        "oracle-db": "tsfont-oracle",
        "os": "tsfont-os",
        "php": "tsfont-php",
        "postgresql": "tsfont-postgresql",
        "postgresql-db": "tsfont-postgresql",
        "python": "tsfont-python",
        "rabbitmq": "tsfont-rabbitmq",
        "redis": "tsfont-redis",
        "resin": "tsfont-resin",
        "router": "tsfont-router",
        "secdev": "tsfont-Webyingyongfanghuxitong",
        "storage": "tsfont-storages",
        "switch": "tsfont-switch",
        "sybase": "tsfont-sybase",
        "sybase-db": "tsfont-sybase",
        "tomcat": "tsfont-tomcat",
        "tuxedo": "tsfont-tuxedo",
        "vcenter": "tsfont-vcenter",
        "weblogic": "tsfont-weblogic",
        "websphere": "tsfont-websphere",
        "windows": "tsfont-windows",
        "zookeeper": "tsfont-zookeeper",
    }
    if name in nameToIconMap:
        return nameToIconMap[name]
    else:
        return None


def getDefaultThreshold(dictPath, name, obj_type):
    threshold = []
    jsonFile = dictPath + "/inspect_default_threshold.json"
    if os.path.isfile(jsonFile):
        data = {}
        with open(jsonFile, "r", encoding="utf-8") as f:
            data = json.load(f)
            f.close()
        if name in data:
            threshold = data[name]
        else:
            if obj_type in data:
                threshold = data[obj_type]
    else:
        print("ERROR: Not found json file: inspect_default_threshold.json .")
    return threshold


def checkThresholdExist(threshold, dbThresholds):
    # "$.MOUNT_POINTS[NAME startswith \"/\"].\"USED%\" > 10"
    # "$.MOUNT_POINTS.\"USED%\" >= 90"
    exist = False
    localRule = threshold["rule"]
    localKey = ""
    if "[" in localRule:
        localKey = localRule[2 : localRule.index("[")]
    else:
        localKey = localRule.split(".")
        localKey = localKey[1]

    for dbThreshold in dbThresholds:
        dbRule = dbThreshold["rule"]
        dbKey = ""
        if "[" in dbRule:
            dbKey = dbRule[2 : dbRule.index("[")]
        else:
            dbKey = dbRule.split(".")
            dbKey = dbKey[1]

        if localKey == dbKey:
            exist = True
            break
    return exist


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--lang", default="zh_CN", help="Language")
    parser.add_argument(
        "--action",
        default="savetodb",
        help="Actions:（savetodb|append|remove|beautify|dropcollection）",
    )
    parser.add_argument("--filter", default="{}", help="Match filter")
    parser.add_argument("--content", default="", help="Append Json")

    args = parser.parse_args()
    lang = args.lang
    action = args.action

    binPaths = os.path.split(os.path.realpath(__file__))
    dictPath = os.path.abspath(binPaths[0])

    dirs = os.listdir(dictPath)

    objTypeList = []
    dbclient = None
    collection = None
    inspectDef = None
    if action == "savetodb":
        import AutoExecUtils

        (dbclient, db) = AutoExecUtils.getDB()
        table = "_dictionary"
        collection = db[table]
        inspectDef = db["_inspectdef"]

    for dfile in dirs:
        jsonFile = dictPath + "/" + dfile

        info = dfile.split(".")
        if len(info) == 3 and info[1] == lang:
            name = info[0]
            hasError = False
            data = {}

            if action not in ("dropcollection", "clearcollection"):
                print("INFO: Try to load json file:", dfile, "...")
                with open(jsonFile, "r", encoding="utf-8") as f:
                    data = json.load(f)
                    f.close()

                if "collection" not in data:
                    hasError = True
                    print("ERROR: Object decription data does not defined attribue 'collection'.\n")
                if "filter" not in data:
                    hasError = True
                    print("ERROR: Object decription data does not defined attribue 'filter'.\n")
                if "label" not in data:
                    hasError = True
                    print("ERROR: Object decription data does not defined attribue 'label'.\n")

            if action == "savetodb":
                print("INFO: Import", dfile, "start...")
                if hasError == False:
                    for field in data.get("fields", []):
                        correctTypeDef(field)

                    tableName = data["collection"]
                    objCat = tableName[8:]
                    colFilter = data.get("filter")
                    objType = None
                    if colFilter is not None:
                        objType = colFilter.get("_OBJ_TYPE")
                    if objType is not None:
                        objTypeList.append({"_OBJ_CATETORY": objCat, "_OBJ_TYPE": objType})
                    collection.delete_one({"name": name})

                    data["name"] = name
                    collection.replace_one({"name": name}, data, upsert=True)
                    collection.create_index([("name", 1)], name="idx_pk", unique=True)
                    print("INFO: Import", dfile, "success.")

                    # 巡检文件内定义的默认阀值
                    defaultThreshold = getDefaultThreshold(dictPath, data["label"], tableName)
                    print("INFO: {} inspect plugin default threshold Loaded.".format(name))
                    # 获取已经选择进入巡检报告定义的字段
                    inspectObj = inspectDef.find_one({"name": name})
                    # 如果巡检定义不存在则自动生成一个空的定义
                    if inspectObj is None:
                        inspectObj = {
                            "name": name,
                            "label": data["label"],
                            "icon": getIcon(name),
                            "collection": data["collection"],
                            "filter": data["filter"],
                            "fields": [],
                            "thresholds": defaultThreshold,
                        }
                    else:
                        inspectObj["icon"] = getIcon(name)
                        inspectObj["label"] = data["label"]
                        inspectObj["collection"] = data["collection"]
                        inspectObj["filter"] = data["filter"]
                        # 合并已存在阀值和文件定义默认阀值
                        dbThreshold = inspectObj["thresholds"]
                        if len(dbThreshold) == 0:
                            inspectObj["thresholds"] = defaultThreshold
                        else:
                            newThreshold = dbThreshold.copy()
                            for threshold in defaultThreshold:
                                if checkThresholdExist(threshold, dbThreshold) == False:
                                    newThreshold.append(threshold)
                            inspectObj["thresholds"] = newThreshold

                    # 获取已经定义的fields
                    inspectFields = []
                    inspectFieldsMap = {}
                    inspectFields = inspectObj["fields"]
                    for fieldObj in inspectFields:
                        inspectFieldsMap[fieldObj["name"]] = 1

                    # 如果集合定义中存在新的字段不在巡检定义里，则自动加入巡检定义中
                    for collectFieldObj in data["fields"]:
                        if collectFieldObj["name"] not in inspectFieldsMap:
                            inspectFields.append({"name": collectFieldObj["name"], "selected": 1})

                    inspectDef.replace_one({"name": name}, inspectObj, upsert=True)
                    inspectDef.create_index([("name", 1)], name="idx_pk", unique=True)
                    print("INFO: Update inspect define for ", dfile, "success.")
                else:
                    print("ERROR: Import", dfile, "failed.")

            elif action == "remove":
                matchFilter = args.filter
                filterObj = json.loads(matchFilter)
                filterMatched = True
                for filterKey in filterObj.keys():
                    if filterKey not in data or data[filterKey] != filterObj[filterKey]:
                        print("INFO: " + data[filterKey] + " not match filter " + matchFilter)
                        filterMatched = False
                        break
                if not filterMatched:
                    continue

                removeKey = args.content
                newFields = []
                fields = data["fields"]
                for field in fields:
                    if "name" in field and field["name"] == removeKey:
                        print("INFO: Remove field " + removeKey + " from " + dfile)
                        continue
                    else:
                        newFields.append(field)
                data["fields"] = newFields
                with open(jsonFile, "w", encoding="utf-8") as f:
                    json.dump(data, f, indent=4, ensure_ascii=False)
                    f.close()

            elif action == "append":
                content = args.content
                if content != "":
                    print("INFO: Append entry to ", dfile, "start...")

                    matchFilter = args.filter
                    filterObj = json.loads(matchFilter)
                    filterMatched = True
                    for filterKey in filterObj.keys():
                        if filterKey not in data or data[filterKey] != filterObj[filterKey]:
                            print("INFO: " + data[filterKey] + " not match filter " + matchFilter)
                            filterMatched = False
                            break
                    if not filterMatched:
                        continue

                    jsonObj = json.loads(content)
                    if "name" not in jsonObj:
                        print("ERROR: Attribute name not in json:", content)
                    if "desc" not in jsonObj:
                        print("ERROR: Attribute desc not in json:", content)
                    if "type" not in jsonObj:
                        print("ERROR: Attribute desc not in json:", content)
                    else:
                        typeVal = jsonObj["type"]
                        if typeVal not in (
                            "String",
                            "JsonArray",
                            "JsonObject",
                            "Int",
                            "Float",
                        ):
                            print(
                                "ERROR: Invalid attribute type:" + typeVal,
                                ", only support String|JsonArray|JsonObject|Int|Float",
                            )

                    data["fields"].append(jsonObj)
                    with open(jsonFile, "w", encoding="utf-8") as f:
                        json.dump(data, f, indent=4, ensure_ascii=False)
                        f.close()

            elif action == "beautify":
                print("INFO: Beautify ", dfile, "start...")
                fields = data["fields"]

                fieldsMap = {}
                for field in fields:
                    fieldName = field["name"]
                    fieldsMap[fieldName] = field

                newFields = []
                for defaultKey in getDefaultFieldKeys():
                    if defaultKey in fieldsMap:
                        correctTypeDef(fieldsMap[defaultKey])
                        newFields.append(fieldsMap[defaultKey])
                        del fieldsMap[defaultKey]

                leftKeys = sorted(fieldsMap.keys())
                for key in leftKeys:
                    correctTypeDef(fieldsMap[key])
                    newFields.append(fieldsMap[key])

                data["fields"] = newFields
                with open(jsonFile, "w", encoding="utf-8") as f:
                    json.dump(data, f, indent=4, ensure_ascii=False)
                    f.close()
                    print("INFO: Beautify ", dfile, "success.")
            elif action == "dropcollection":
                import AutoExecUtils

                (dbclient, db) = AutoExecUtils.getDB()
                colName = args.content
                if colName == "all":
                    for colName in (
                        "ACCESSENDPOINT",
                        "APPLICATION",
                        "CLUSTER",
                        "DB",
                        "DBINS",
                        "EMPTY",
                        "FCDEV",
                        "HOST",
                        "INS",
                        "K8S",
                        "LOADBALANCER",
                        "NETDEV",
                        "OS",
                        "SECDEV",
                        "STORAGE",
                        "UNKNOWN",
                        "VIRTUALIZED",
                    ):
                        col = db["COLLECT_" + colName]
                        col.drop()
                else:
                    col = db[colName]
                    col.drop()
            elif action == "clearcollection":
                import AutoExecUtils

                (dbclient, db) = AutoExecUtils.getDB()
                colName = args.content
                if colName == "all":
                    for colName in (
                        "ACCESSENDPOINT",
                        "APPLICATION",
                        "CLUSTER",
                        "DB",
                        "DBINS",
                        "EMPTY",
                        "FCDEV",
                        "HOST",
                        "INS",
                        "K8S",
                        "LOADBALANCER",
                        "NETDEV",
                        "OS",
                        "SECDEV",
                        "STORAGE",
                        "UNKNOWN",
                        "VIRTUALIZED",
                    ):
                        col = db["COLLECT_" + colName]
                        col.delete_many({})
                else:
                    col = db[colName]
                    col.delete_many({})
            else:
                print("ERROR: Action " + action + "not supported.")
    print("\n")
    print("INFO: Object type list imported.\n", end="")
    for objTypeItem in objTypeList:
        print(
            objTypeItem["_OBJ_CATETORY"] + "/" + objTypeItem["_OBJ_TYPE"] + "\n",
            end="",
        )
    # 关闭连接
    if dbclient is not None:
        dbclient.close()
